// main.cpp
// 简单的Vulkan计算直方图示例，用于处理16位灰度DICOM图像
// 使用CMake构建（需要glslc编译着色器）

#include <vulkan/vulkan.h>
#ifdef __APPLE__
#include <vulkan/vulkan_metal.h>  // macOS平台的Metal扩展支持
#endif
#include <dcmtk/config/osconfig.h>  // DCMTK库配置
#include <dcmtk/dcmdata/dctk.h>     // DCMTK数据工具库
#include <dcmtk/dcmimgle/dcmimage.h> // DCMTK图像处理库
#include <iostream>  // 标准输入输出流
#include <fstream>   // 文件输入输出流
#include <vector>    // 动态数组容器
#include <cassert>   // 断言工具
#include <cstring>   // C字符串操作
#include <optional>  // 可选值类型
#include <algorithm> // 算法库

// 使用GLFW创建窗口并与Vulkan集成
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>

// 简单的辅助函数：读取16位灰度DICOM文件到uint16_t向量中
bool load_dicom16(const std::string &path, int &width, int &height, std::vector<uint16_t> &out_pixels) {
    // 创建DICOM文件格式对象
    DcmFileFormat fileFormat;
    // 加载DICOM文件
    OFCondition status = fileFormat.loadFile(path.c_str());
    if (!status.good()) {
        std::cerr << "Failed to load DICOM file: " << status.text() << "\n";
        return false;
    }

    // 获取DICOM数据集
    DcmDataset *dataset = fileFormat.getDataset();

    // 获取图像尺寸信息
    Uint16 cols, rows;
    if (dataset->findAndGetUint16(DCM_Columns, cols).bad() ||
        dataset->findAndGetUint16(DCM_Rows, rows).bad()) {
        std::cerr << "Failed to get image dimensions\n";
        return false;
    }

    width = cols;  // 图像宽度
    height = rows; // 图像高度
    size_t pixelCount = size_t(width) * size_t(height); // 像素总数

    // 检查是否为灰度图像（单通道）
    Uint16 samplesPerPixel;
    if (dataset->findAndGetUint16(DCM_SamplesPerPixel, samplesPerPixel).bad() ||
        samplesPerPixel != 1) {
        std::cerr << "Not a single channel grayscale image\n";
        return false;
    }

    // 获取位深度信息
    Uint16 bitsAllocated, bitsStored;
    if (dataset->findAndGetUint16(DCM_BitsAllocated, bitsAllocated).bad() ||
        dataset->findAndGetUint16(DCM_BitsStored, bitsStored).bad()) {
        std::cerr << "Failed to get bit information\n";
        return false;
    }

    if (bitsAllocated != 16) {
        std::cerr << "Not a 16-bit image\n";
        return false;
    }

    // 使用DicomImage类正确提取像素数据
    DicomImage dcmImage(fileFormat.getDataset(), EXS_Unknown, 0x0);
    if (dcmImage.getStatus() != EIS_Normal) {
        std::cerr << "Failed to create DicomImage\n";
        return false;
    }

    if (dcmImage.getDepth() != 16) {
        std::cerr << "Not a 16-bit image\n";
        return false;
    }

    // 获取像素数据
    const void *pixelData = dcmImage.getOutputData(16);
    if (pixelData == nullptr) {
        std::cerr << "Failed to get pixel data\n";
        return false;
    }

    // 将像素数据复制到输出向量
    const Uint16 *pixelData16 = static_cast<const Uint16*>(pixelData);
    out_pixels.resize(pixelCount);
    for (size_t i = 0; i < pixelCount; ++i) {
        out_pixels[i] = static_cast<uint16_t>(pixelData16[i]);
    }

    std::cout << "Loaded DICOM " << width << "x" << height << ", bits=" << bitsAllocated << ", pixels=" << pixelCount << "\n";
    return true;
}

// 工具宏：Vulkan错误检查
// 如果Vulkan函数返回错误，打印错误信息并终止程序
#define VK_CHECK(x) do { VkResult res = (x); if (res != VK_SUCCESS) { \
    std::cerr << "Vulkan error at " << __LINE__ << ": " << res << std::endl; std::abort(); } } while(0)

// 查找合适的内存类型
// 根据物理设备、内存类型过滤器和内存属性查找合适的内存类型索引
uint32_t findMemoryType(VkPhysicalDevice phys, uint32_t typeFilter, VkMemoryPropertyFlags props) {
    VkPhysicalDeviceMemoryProperties memProps;
    vkGetPhysicalDeviceMemoryProperties(phys, &memProps);
    for (uint32_t i = 0; i < memProps.memoryTypeCount; ++i) {
        // 检查内存类型是否匹配过滤器和属性要求
        if ((typeFilter & (1u << i)) && (memProps.memoryTypes[i].propertyFlags & props) == props) {
            return i;
        }
    }
    throw std::runtime_error("Failed to find memory type");
}

int main(int argc, char** argv) {
    // 检查命令行参数
    if (argc < 2) {
        std::cout << "Usage: " << argv[0] << " input.dcm\n";
        return 1;
    }
    
    std::string input_path = argv[1]; // 输入DICOM文件路径
    int width=0, height=0;           // 图像宽度和高度
    std::vector<uint16_t> pixels16;  // 16位像素数据
    
    // 加载DICOM文件
    if (!load_dicom16(input_path, width, height, pixels16)) {
        std::cerr << "Failed to load DICOM\n";
        return 1;
    }
    
    size_t pixelCount = size_t(width) * size_t(height); // 像素总数
    std::cout << "Loaded DICOM " << width << "x" << height << ", pixels=" << pixelCount << "\n";

    // 直接使用16位像素数据（着色器现在支持uint16_t类型）
    // 不再需要转换为32位，节省内存带宽

    // Vulkan基础初始化（实例、物理设备、逻辑设备、队列）
    VkInstance instance; // Vulkan实例
    {
        // 先初始化GLFW，以便查询当前平台所需的实例扩展
        if (!glfwInit()) {
            std::cerr << "Failed to init GLFW" << std::endl;
            return 1;
        }

        // 应用程序信息结构体
        VkApplicationInfo appInfo{VK_STRUCTURE_TYPE_APPLICATION_INFO};
        appInfo.pApplicationName = "VulkanHistogram16";      // 应用程序名称
        appInfo.applicationVersion = VK_MAKE_VERSION(1,0,0); // 应用程序版本
        appInfo.pEngineName = "NoEngine";                    // 引擎名称
        appInfo.engineVersion = VK_MAKE_VERSION(1,0,0);      // 引擎版本
        appInfo.apiVersion = VK_API_VERSION_1_1;             // Vulkan API版本

        // 通过GLFW获取跨平台所需的实例扩展（GLFW会根据平台返回对应的WSI扩展）
        uint32_t glfwExtCount = 0;
        const char** glfwExts = glfwGetRequiredInstanceExtensions(&glfwExtCount);
        if (!glfwExts || glfwExtCount == 0) {
            std::cerr << "GLFW did not provide required Vulkan instance extensions" << std::endl;
            return 1;
        }
        std::vector<const char*> instanceExts(glfwExts, glfwExts + glfwExtCount);
#ifdef __APPLE__
        // MoltenVK/macOS 需要可移植性枚举扩展
        instanceExts.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
#endif

        // 实例创建信息结构体
        VkInstanceCreateInfo createInfo{VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO};
        createInfo.pApplicationInfo = &appInfo;           // 应用程序信息
        createInfo.enabledExtensionCount = static_cast<uint32_t>(instanceExts.size());
        createInfo.ppEnabledExtensionNames = instanceExts.data(); // 启用的扩展名称数组
#ifdef __APPLE__
        // 为MoltenVK启用可移植性枚举
        createInfo.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
#else
        createInfo.flags = 0;
#endif

        // 创建Vulkan实例
        VK_CHECK( vkCreateInstance(&createInfo, nullptr, &instance) );
    }

    // 创建窗口与Vulkan表面（GLFW已在创建实例前初始化）
    GLFWwindow* window = nullptr;
    VkSurfaceKHR surface = VK_NULL_HANDLE;
    {
        glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
        int winW = std::max(64, width);
        int winH = std::max(64, height);
        window = glfwCreateWindow(winW, winH, "DICOM Viewer", nullptr, nullptr);
        if (!window) {
            std::cerr << "Failed to create GLFW window" << std::endl;
            return 1;
        }
        VK_CHECK( glfwCreateWindowSurface(instance, window, nullptr, &surface) );
    }

    // 选择同时具备图形、计算与呈现支持的物理设备与队列族
    uint32_t physCount = 0;
    VK_CHECK( vkEnumeratePhysicalDevices(instance, &physCount, nullptr) );
    if (physCount == 0) { std::cerr << "No Vulkan physical devices\n"; return 1; }
    std::vector<VkPhysicalDevice> physDevices(physCount);
    VK_CHECK( vkEnumeratePhysicalDevices(instance, &physCount, physDevices.data()) );
    VkPhysicalDevice physical = VK_NULL_HANDLE; // 物理设备
    uint32_t selectedQueueFamily = UINT32_MAX;  // 选中的队列族索引
    for (auto &p : physDevices) {
        uint32_t qCount = 0;
        vkGetPhysicalDeviceQueueFamilyProperties(p, &qCount, nullptr);
        std::vector<VkQueueFamilyProperties> qprops(qCount);
        vkGetPhysicalDeviceQueueFamilyProperties(p, &qCount, qprops.data());
        for (uint32_t i = 0; i < qCount; ++i) {
            VkBool32 presentSupport = VK_FALSE;
            vkGetPhysicalDeviceSurfaceSupportKHR(p, i, surface, &presentSupport);
            if (presentSupport && (qprops[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && (qprops[i].queueFlags & VK_QUEUE_COMPUTE_BIT)) {
                physical = p;
                selectedQueueFamily = i;
                break;
            }
        }
        if (physical != VK_NULL_HANDLE) break;
    }
    if (physical == VK_NULL_HANDLE) { std::cerr << "No suitable physical device with graphics+compute+present\n"; return 1; }

    // 使用选中的队列族（同时支持graphics/compute/present）
    std::optional<uint32_t> computeQueueFamily = selectedQueueFamily;
    float qPriority = 1.0f; // 队列优先级
    VkDevice device;        // 逻辑设备
    VkQueue queue;          // 队列
    {
        // 队列创建信息
        VkDeviceQueueCreateInfo qci{VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO};
        qci.queueFamilyIndex = *computeQueueFamily; // 队列族索引（graphics+compute+present）
        qci.queueCount = 1;                         // 队列数量
        qci.pQueuePriorities = &qPriority;          // 队列优先级

        // 设备创建信息
        VkDeviceCreateInfo dci{VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO};
        dci.queueCreateInfoCount = 1;        // 队列创建信息数量
        dci.pQueueCreateInfos = &qci;        // 队列创建信息数组

        // 启用交换链扩展（以及可移植性子集，如果可用）
        uint32_t devExtCount = 0;
        vkEnumerateDeviceExtensionProperties(physical, nullptr, &devExtCount, nullptr);
        std::vector<VkExtensionProperties> devExtProps(devExtCount);
        vkEnumerateDeviceExtensionProperties(physical, nullptr, &devExtCount, devExtProps.data());
        auto hasExt = [&](const char* name){
            for (const auto &e : devExtProps) {
                if (std::strcmp(e.extensionName, name) == 0) return true;
            }
            return false;
        };
        std::vector<const char*> devExts;
        devExts.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
#ifdef __APPLE__
        if (hasExt("VK_KHR_portability_subset")) {
            devExts.push_back("VK_KHR_portability_subset");
        }
#endif
        dci.enabledExtensionCount = static_cast<uint32_t>(devExts.size());
        dci.ppEnabledExtensionNames = devExts.data();

        VK_CHECK( vkCreateDevice(physical, &dci, nullptr, &device) ); // 创建逻辑设备
        vkGetDeviceQueue(device, *computeQueueFamily, 0, &queue);     // 获取队列
    }

    // 工具lambda函数：创建缓冲区并分配内存
    // 参数：大小、使用标志、内存属性、缓冲区引用、内存引用
    auto createBuffer = [&](VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags props,
                            VkBuffer &buf, VkDeviceMemory &mem) {
        // 缓冲区创建信息
        VkBufferCreateInfo bci{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};
        bci.size = size;                    // 缓冲区大小
        bci.usage = usage;                  // 缓冲区使用方式
        bci.sharingMode = VK_SHARING_MODE_EXCLUSIVE; // 独占共享模式
        VK_CHECK( vkCreateBuffer(device, &bci, nullptr, &buf) ); // 创建缓冲区

        // 获取缓冲区内存需求
        VkMemoryRequirements memReq;
        vkGetBufferMemoryRequirements(device, buf, &memReq);
        uint32_t memType = findMemoryType(physical, memReq.memoryTypeBits, props); // 查找内存类型
        // 内存分配信息
        VkMemoryAllocateInfo mai{VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};
        mai.allocationSize = memReq.size;      // 分配大小
        mai.memoryTypeIndex = memType;         // 内存类型索引
        VK_CHECK( vkAllocateMemory(device, &mai, nullptr, &mem) ); // 分配内存
        VK_CHECK( vkBindBufferMemory(device, buf, mem, 0) );       // 绑定缓冲区和内存
    };

    // 创建像素上传的暂存缓冲区（主机可见）和用于计算读取的设备本地缓冲区
    VkBuffer pixelStagingBuf = VK_NULL_HANDLE; // 像素暂存缓冲区
    VkDeviceMemory pixelStagingMem = VK_NULL_HANDLE; // 像素暂存内存
    VkBuffer pixelDeviceBuf = VK_NULL_HANDLE;        // 像素设备缓冲区
    VkDeviceMemory pixelDeviceMem = VK_NULL_HANDLE;  // 像素设备内存

    VkDeviceSize pixelBufferSize = sizeof(uint16_t) * pixelCount; // 像素缓冲区大小（存储为uint16）
    createBuffer(pixelBufferSize,
                 VK_BUFFER_USAGE_TRANSFER_SRC_BIT, // 传输源使用方式
                 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, // 主机可见且一致
                 pixelStagingBuf, pixelStagingMem);

    createBuffer(pixelBufferSize,
                 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, // 存储缓冲区和传输目标
                 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, // 设备本地内存
                 pixelDeviceBuf, pixelDeviceMem);

    // 映射暂存缓冲区并复制像素数据
    {
        void* map = nullptr;
        VK_CHECK( vkMapMemory(device, pixelStagingMem, 0, pixelBufferSize, 0, &map) ); // 映射内存
        // 复制像素数据
        std::memcpy(map, pixels16.data(), (size_t)pixelBufferSize);
        vkUnmapMemory(device, pixelStagingMem); // 取消映射
    }

    // 直方图缓冲区：设备本地存储，以及用于回读的主机可见暂存区
    const uint32_t HIST_BINS = 65536; // 直方图桶数量（16位图像）
    VkDeviceSize histBufferSize = sizeof(uint32_t) * HIST_BINS; // 直方图缓冲区大小

    VkBuffer histDeviceBuf = VK_NULL_HANDLE; // 直方图设备缓冲区
    VkDeviceMemory histDeviceMem = VK_NULL_HANDLE; // 直方图设备内存
    createBuffer(histBufferSize,
                 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, // 存储、传输源、传输目标
                 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, // 设备本地内存
                 histDeviceBuf, histDeviceMem);

    VkBuffer histStagingBuf = VK_NULL_HANDLE; // 直方图暂存缓冲区
    VkDeviceMemory histStagingMem = VK_NULL_HANDLE; // 直方图暂存内存
    createBuffer(histBufferSize,
                 VK_BUFFER_USAGE_TRANSFER_DST_BIT, // 传输目标
                 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, // 主机可见且一致
                 histStagingBuf, histStagingMem);

    // 创建命令池和命令缓冲区用于复制和计算
    VkCommandPool cmdPool; // 命令池
    {
        VkCommandPoolCreateInfo cpci{VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO};
        cpci.queueFamilyIndex = *computeQueueFamily; // 队列族索引
        cpci.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; // 重置命令缓冲区标志
        VK_CHECK( vkCreateCommandPool(device, &cpci, nullptr, &cmdPool) );
    }
    VkCommandBuffer cmdBuf; // 命令缓冲区
    {
        VkCommandBufferAllocateInfo cbai{VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO};
        cbai.commandPool = cmdPool;              // 命令池
        cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; // 主命令缓冲区级别
        cbai.commandBufferCount = 1;             // 命令缓冲区数量
        VK_CHECK( vkAllocateCommandBuffers(device, &cbai, &cmdBuf) );
    }

    // 加载SPIR-V着色器（由glslc编译成shader.comp.spv），支持从当前目录或可执行文件目录查找
    std::vector<char> shaderSpv; // 着色器SPIR-V字节码
    {
        std::string exePath = std::string(argv[0]);
        size_t slash = exePath.find_last_of("/\\");
        std::string exeDir = (slash != std::string::npos) ? exePath.substr(0, slash) : std::string(".");
        std::ifstream spv("./shader.comp.spv", std::ios::binary); // 先在当前目录查找
        if (!spv) spv.open("shader.comp.spv", std::ios::binary);
        if (!spv) {
            std::string alt = exeDir + "/shader.comp.spv";
            spv.open(alt, std::ios::binary);
        }
        if (!spv) {
            std::cerr << "Failed to open shader.comp.spv. Tried ./, ./, and executable dir: " << exeDir << "\n";
            return 1;
        }
        spv.seekg(0, std::ios::end); // 移动到文件末尾
        size_t size = spv.tellg();   // 获取文件大小
        spv.seekg(0, std::ios::beg); // 移动到文件开头
        shaderSpv.resize(size);      // 调整向量大小
        spv.read(shaderSpv.data(), size); // 读取数据
    }

    VkShaderModule shaderModule; // 着色器模块
    {
        VkShaderModuleCreateInfo smci{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO};
        smci.codeSize = shaderSpv.size(); // 代码大小
        smci.pCode = reinterpret_cast<const uint32_t*>(shaderSpv.data()); // SPIR-V代码指针
        VK_CHECK( vkCreateShaderModule(device, &smci, nullptr, &shaderModule) );
    }

    // 描述符集布局：绑定0像素（存储），绑定1直方图（存储）
    VkDescriptorSetLayout descLayout; // 描述符集布局
    {
        VkDescriptorSetLayoutBinding b0{}; // 绑定0：像素缓冲区
        b0.binding = 0;                    // 绑定点索引
        b0.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; // 存储缓冲区类型
        b0.descriptorCount = 1;            // 描述符数量
        b0.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; // 计算着色器阶段

        VkDescriptorSetLayoutBinding b1{}; // 绑定1：直方图缓冲区
        b1.binding = 1;                    // 绑定点索引
        b1.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; // 存储缓冲区类型
        b1.descriptorCount = 1;            // 描述符数量
        b1.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; // 计算着色器阶段

        VkDescriptorSetLayoutBinding bindings[2] = {b0, b1}; // 绑定数组
        VkDescriptorSetLayoutCreateInfo dslci{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
        dslci.bindingCount = 2;        // 绑定数量
        dslci.pBindings = bindings;    // 绑定数组指针
        VK_CHECK( vkCreateDescriptorSetLayout(device, &dslci, nullptr, &descLayout) );
    }

    // 管道布局与推送常量
    VkPipelineLayout pipelineLayout; // 管道布局
    {
        VkPushConstantRange pcr{}; // 推送常量范围
        pcr.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; // 计算着色器阶段
        pcr.offset = 0;           // 偏移量
        pcr.size = sizeof(uint32_t); // 大小（numPixels）

        VkPipelineLayoutCreateInfo plci{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
        plci.setLayoutCount = 1;              // 布局数量
        plci.pSetLayouts = &descLayout;       // 描述符集布局数组
        plci.pushConstantRangeCount = 1;      // 推送常量范围数量
        plci.pPushConstantRanges = &pcr;      // 推送常量范围数组
        VK_CHECK( vkCreatePipelineLayout(device, &plci, nullptr, &pipelineLayout) );
    }

    // 创建描述符池并分配描述符集
    VkDescriptorPool descPool;  // 描述符池
    VkDescriptorSet descSet;    // 描述符集
    {
        VkDescriptorPoolSize poolSizes[2]; // 池大小数组
        poolSizes[0].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; // 存储缓冲区类型
        poolSizes[0].descriptorCount = 2;  // 描述符数量
        poolSizes[1].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; // 存储缓冲区类型
        poolSizes[1].descriptorCount = 2;  // 描述符数量

        VkDescriptorPoolCreateInfo dpci{VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO};
        dpci.maxSets = 1;              // 最大集合数量
        dpci.poolSizeCount = 1;        // 池大小数量
        VkDescriptorPoolSize ps{};     // 池大小
        ps.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; // 类型
        ps.descriptorCount = 2;        // 描述符数量
        dpci.pPoolSizes = &ps;         // 池大小数组
        dpci.poolSizeCount = 1;        // 池大小数量
        VK_CHECK( vkCreateDescriptorPool(device, &dpci, nullptr, &descPool) );

        VkDescriptorSetAllocateInfo dsai{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO};
        dsai.descriptorPool = descPool; // 描述符池
        dsai.descriptorSetCount = 1;    // 集合数量
        dsai.pSetLayouts = &descLayout; // 布局数组
        VK_CHECK( vkAllocateDescriptorSets(device, &dsai, &descSet) );
    }

    // 更新描述符集
    {
        VkDescriptorBufferInfo pixelBufInfo{}; // 像素缓冲区信息
        pixelBufInfo.buffer = pixelDeviceBuf;  // 缓冲区对象
        pixelBufInfo.offset = 0;               // 偏移量
        pixelBufInfo.range = pixelBufferSize;  // 范围

        VkDescriptorBufferInfo histBufInfo{};  // 直方图缓冲区信息
        histBufInfo.buffer = histDeviceBuf;    // 缓冲区对象
        histBufInfo.offset = 0;                // 偏移量
        histBufInfo.range = histBufferSize;    // 范围

        VkWriteDescriptorSet writes[2]{};      // 写入描述符集数组
        writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; // 结构类型
        writes[0].dstSet = descSet;            // 目标描述符集
        writes[0].dstBinding = 0;              // 目标绑定点
        writes[0].descriptorCount = 1;         // 描述符数量
        writes[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; // 描述符类型
        writes[0].pBufferInfo = &pixelBufInfo; // 缓冲区信息

        writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; // 结构类型
        writes[1].dstSet = descSet;            // 目标描述符集
        writes[1].dstBinding = 1;              // 目标绑定点
        writes[1].descriptorCount = 1;         // 描述符数量
        writes[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; // 描述符类型
        writes[1].pBufferInfo = &histBufInfo;  // 缓冲区信息

        vkUpdateDescriptorSets(device, 2, writes, 0, nullptr); // 更新描述符集
    }

    // 创建计算管道
    VkPipeline pipeline; // 管道对象
    {
        VkPipelineShaderStageCreateInfo pssci{VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO};
        pssci.stage = VK_SHADER_STAGE_COMPUTE_BIT; // 计算着色器阶段
        pssci.module = shaderModule;               // 着色器模块
        pssci.pName = "main";                      // 入口点名称

        VkComputePipelineCreateInfo cpci{VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO};
        cpci.stage = pssci;              // 着色器阶段信息
        cpci.layout = pipelineLayout;    // 管道布局
        VK_CHECK( vkCreateComputePipelines(device, VK_NULL_HANDLE, 1, &cpci, nullptr, &pipeline) );
    }

    // 准备命令缓冲区：清零直方图（通过fill），复制像素暂存->设备，调度计算，复制直方图设备->暂存
    VkFence fence; // 同步栅栏
    {
        VkFenceCreateInfo fci{VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
        fci.flags = 0; // 栅栏标志
        VK_CHECK( vkCreateFence(device, &fci, nullptr, &fence) );
    }

    // 开始命令缓冲区记录
    VkCommandBufferBeginInfo cbbi{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
    cbbi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; // 一次性提交使用
    VK_CHECK( vkBeginCommandBuffer(cmdBuf, &cbbi) );

    // 用零填充直方图设备缓冲区
    vkCmdFillBuffer(cmdBuf, histDeviceBuf, 0, histBufferSize, 0);

    // 内存屏障：确保填充完成后再让计算着色器读取
    VkBufferMemoryBarrier fillBarrier{VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER};
    fillBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; // 源访问掩码
    fillBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; // 目标访问掩码
    fillBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // 源队列族索引
    fillBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // 目标队列族索引
    fillBarrier.buffer = histDeviceBuf; // 缓冲区对象
    fillBarrier.offset = 0;             // 偏移量
    fillBarrier.size = histBufferSize;  // 大小
    vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // 管道阶段
                         0, 0, nullptr, 1, &fillBarrier, 0, nullptr);

    // 复制像素暂存 -> 设备
    VkBufferCopy copyRegion{}; // 缓冲区复制区域
    copyRegion.srcOffset = 0;  // 源偏移
    copyRegion.dstOffset = 0;  // 目标偏移
    copyRegion.size = pixelBufferSize; // 复制大小
    vkCmdCopyBuffer(cmdBuf, pixelStagingBuf, pixelDeviceBuf, 1, &copyRegion);

    // 内存屏障：确保复制完成后再让计算着色器读取像素
    VkBufferMemoryBarrier copyBarrier{VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER};
    copyBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; // 源访问掩码
    copyBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;    // 目标访问掩码
    copyBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // 源队列族索引
    copyBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // 目标队列族索引
    copyBarrier.buffer = pixelDeviceBuf; // 缓冲区对象
    copyBarrier.offset = 0;              // 偏移量
    copyBarrier.size = pixelBufferSize;  // 大小
    vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // 管道阶段
                         0, 0, nullptr, 1, &copyBarrier, 0, nullptr);

    // 绑定管道和描述符集
    vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); // 绑定计算管道
    vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout, 0, 1, &descSet, 0, nullptr); // 绑定描述符集

    // 推送常量：numPixels（像素数量）
    uint32_t np = static_cast<uint32_t>(pixelCount);
    vkCmdPushConstants(cmdBuf, pipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(uint32_t), &np);

    // 调度计算：计算调用次数 = 像素数量，local_size_x=256（如着色器中定义）
    const uint32_t localSizeX = 256; // 着色器中的工作组大小
    uint32_t groupCount = (np + localSizeX - 1) / localSizeX; // 工作组数量
    vkCmdDispatch(cmdBuf, groupCount, 1, 1);

    // 内存屏障：确保计算完成且直方图准备好复制
    VkBufferMemoryBarrier compBarrier{VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER};
    compBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; // 源访问掩码
    compBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; // 目标访问掩码
    compBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // 源队列族索引
    compBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // 目标队列族索引
    compBarrier.buffer = histDeviceBuf; // 缓冲区对象
    compBarrier.offset = 0;             // 偏移量
    compBarrier.size = histBufferSize;  // 大小
    vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, // 管道阶段
                         0, 0, nullptr, 1, &compBarrier, 0, nullptr);

    // 复制直方图设备 -> 暂存
    VkBufferCopy histCopy{}; // 直方图复制区域
    histCopy.srcOffset = 0;  // 源偏移
    histCopy.dstOffset = 0;  // 目标偏移
    histCopy.size = histBufferSize; // 复制大小
    vkCmdCopyBuffer(cmdBuf, histDeviceBuf, histStagingBuf, 1, &histCopy);

    VK_CHECK( vkEndCommandBuffer(cmdBuf) ); // 结束命令缓冲区记录

    // 提交命令
    VkSubmitInfo si{VK_STRUCTURE_TYPE_SUBMIT_INFO};
    si.commandBufferCount = 1;      // 命令缓冲区数量
    si.pCommandBuffers = &cmdBuf;   // 命令缓冲区数组
    VK_CHECK( vkQueueSubmit(queue, 1, &si, fence) ); // 提交到队列
    VK_CHECK( vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX) ); // 等待栅栏

    // 映射暂存直方图、打印结果并计算自动窗宽/窗位（WC/WW）
    float autoWC = 0.5f, autoWW = 1.0f; // 归一化到[0,1]
    {
        void* map = nullptr;
        VK_CHECK( vkMapMemory(device, histStagingMem, 0, histBufferSize, 0, &map) ); // 映射内存
        uint32_t* histData = reinterpret_cast<uint32_t*>(map); // 转换为uint32指针
        // 总数
        uint64_t total = 0;
        for (uint32_t i = 0; i < HIST_BINS; ++i) total += histData[i];
        std::cout << "Histogram total counts = " << total << "\n";
        // 打印前一些非零桶（调试）
        std::cout << "Non-zero bins (value: count) - listing up to first 100 entries:\n";
        int printed = 0;
        for (uint32_t i = 0; i < HIST_BINS && printed < 100; ++i) {
            if (histData[i] != 0) {
                std::cout << i << ": " << histData[i] << "\n";
                printed++;
            }
        }
        if (printed == 0) std::cout << "(all bins zero?)\n";

        // 自动窗宽/窗位：使用直方图百分位（0.5%~99.5%）估计有效范围
        uint64_t lowTarget = (uint64_t)(total * 0.005);   // 0.5%
        uint64_t highTarget = (uint64_t)(total * 0.995);  // 99.5%
        uint64_t accum = 0;
        uint32_t lowBin = 0, highBin = HIST_BINS - 1;
        for (uint32_t i = 0; i < HIST_BINS; ++i) {
            accum += histData[i];
            if (accum >= lowTarget) { lowBin = i; break; }
        }
        accum = 0;
        for (int i = HIST_BINS - 1; i >= 0; --i) {
            accum += histData[i];
            if (accum >= (total - highTarget)) { highBin = (uint32_t)i; break; }
        }
        if (highBin <= lowBin) {
            // 退化情况：使用最小/最大非零桶
            for (uint32_t i = 0; i < HIST_BINS; ++i) if (histData[i]) { lowBin = i; break; }
            for (int i = HIST_BINS - 1; i >= 0; --i) if (histData[i]) { highBin = (uint32_t)i; break; }
        }
        // 归一化（R16_UNORM）并计算 WC/WW
        if (highBin > lowBin) {
            float lowN = (float)lowBin / 65535.0f;
            float highN = (float)highBin / 65535.0f;
            autoWC = 0.5f * (lowN + highN);
            autoWW = std::max(highN - lowN, 1e-6f);
        }
        std::cout << "Auto WindowCenter/Width -> WC=" << autoWC << ", WW=" << autoWW
                  << " (bins " << lowBin << ".." << highBin << ")\n";
        vkUnmapMemory(device, histStagingMem);
    }

    // ============ 将DICOM像素上传为采样纹理（R16_UNORM） ============
    VkImage texImage = VK_NULL_HANDLE;
    VkDeviceMemory texMem = VK_NULL_HANDLE;
    VkImageView texView = VK_NULL_HANDLE;
    VkSampler texSampler = VK_NULL_HANDLE;
    {
        // 创建图像
        VkImageCreateInfo ici{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
        ici.imageType = VK_IMAGE_TYPE_2D;
        ici.format = VK_FORMAT_R16_UNORM;
        ici.extent = { (uint32_t)width, (uint32_t)height, 1 };
        ici.mipLevels = 1;
        ici.arrayLayers = 1;
        ici.samples = VK_SAMPLE_COUNT_1_BIT;
        ici.tiling = VK_IMAGE_TILING_OPTIMAL;
        ici.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
        ici.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
        ici.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
        VK_CHECK( vkCreateImage(device, &ici, nullptr, &texImage) );

        VkMemoryRequirements memReq{};
        vkGetImageMemoryRequirements(device, texImage, &memReq);
        uint32_t memType = findMemoryType(physical, memReq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
        VkMemoryAllocateInfo mai{VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};
        mai.allocationSize = memReq.size;
        mai.memoryTypeIndex = memType;
        VK_CHECK( vkAllocateMemory(device, &mai, nullptr, &texMem) );
        VK_CHECK( vkBindImageMemory(device, texImage, texMem, 0) );

        // 暂存缓冲区上传像素16位
        VkBuffer texStagingBuf = VK_NULL_HANDLE;
        VkDeviceMemory texStagingMem = VK_NULL_HANDLE;
        VkDeviceSize texSize = sizeof(uint16_t) * pixelCount;
        createBuffer(texSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
                     VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
                     texStagingBuf, texStagingMem);
        void* tmap = nullptr;
        VK_CHECK( vkMapMemory(device, texStagingMem, 0, texSize, 0, &tmap) );
        std::memcpy(tmap, pixels16.data(), (size_t)texSize);
        vkUnmapMemory(device, texStagingMem);

        // 使用现有命令缓冲区进行一次性拷贝与布局转换
        VK_CHECK( vkResetCommandBuffer(cmdBuf, 0) );
        VkCommandBufferBeginInfo binfo{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
        binfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
        VK_CHECK( vkBeginCommandBuffer(cmdBuf, &binfo) );

        // UNDEFINED -> TRANSFER_DST_OPTIMAL
        VkImageMemoryBarrier toDst{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
        toDst.srcAccessMask = 0;
        toDst.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
        toDst.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
        toDst.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
        toDst.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
        toDst.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
        toDst.image = texImage;
        toDst.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
        toDst.subresourceRange.baseMipLevel = 0;
        toDst.subresourceRange.levelCount = 1;
        toDst.subresourceRange.baseArrayLayer = 0;
        toDst.subresourceRange.layerCount = 1;
        vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
                             0, 0, nullptr, 0, nullptr, 1, &toDst);

        // 拷贝缓冲区到图像
        VkBufferImageCopy bic{};
        bic.bufferOffset = 0;
        bic.bufferRowLength = 0;
        bic.bufferImageHeight = 0;
        bic.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
        bic.imageSubresource.mipLevel = 0;
        bic.imageSubresource.baseArrayLayer = 0;
        bic.imageSubresource.layerCount = 1;
        bic.imageOffset = {0,0,0};
        bic.imageExtent = { (uint32_t)width, (uint32_t)height, 1 };
        vkCmdCopyBufferToImage(cmdBuf, texStagingBuf, texImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bic);

        // TRANSFER_DST_OPTIMAL -> SHADER_READ_ONLY_OPTIMAL
        VkImageMemoryBarrier toRead{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
        toRead.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
        toRead.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
        toRead.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
        toRead.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
        toRead.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
        toRead.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
        toRead.image = texImage;
        toRead.subresourceRange = toDst.subresourceRange;
        vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
                             0, 0, nullptr, 0, nullptr, 1, &toRead);

        VK_CHECK( vkEndCommandBuffer(cmdBuf) );
        VK_CHECK( vkResetFences(device, 1, &fence) );
        VkSubmitInfo tsi{VK_STRUCTURE_TYPE_SUBMIT_INFO};
        tsi.commandBufferCount = 1;
        tsi.pCommandBuffers = &cmdBuf;
        VK_CHECK( vkQueueSubmit(queue, 1, &tsi, fence) );
        VK_CHECK( vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX) );

        // 创建图像视图与采样器
        VkImageViewCreateInfo ivci{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
        ivci.image = texImage;
        ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;
        ivci.format = VK_FORMAT_R16_UNORM;
        ivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
        ivci.subresourceRange.baseMipLevel = 0;
        ivci.subresourceRange.levelCount = 1;
        ivci.subresourceRange.baseArrayLayer = 0;
        ivci.subresourceRange.layerCount = 1;
        VK_CHECK( vkCreateImageView(device, &ivci, nullptr, &texView) );

        VkSamplerCreateInfo sci{VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};
        sci.magFilter = VK_FILTER_LINEAR;
        sci.minFilter = VK_FILTER_LINEAR;
        sci.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
        sci.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
        sci.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
        sci.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
        sci.maxAnisotropy = 1.0f;
        sci.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
        sci.unnormalizedCoordinates = VK_FALSE;
        VK_CHECK( vkCreateSampler(device, &sci, nullptr, &texSampler) );

        // 释放暂存缓冲区
        vkDestroyBuffer(device, texStagingBuf, nullptr);
        vkFreeMemory(device, texStagingMem, nullptr);
    }

    // ============ 创建交换链、图像视图、渲染通道、帧缓冲 ============
    VkSwapchainKHR swapchain = VK_NULL_HANDLE;
    VkFormat swapFormat = VK_FORMAT_B8G8R8A8_UNORM;
    VkExtent2D swapExtent{};
    std::vector<VkImage> swapImages;
    std::vector<VkImageView> swapImageViews;
    VkRenderPass renderPass = VK_NULL_HANDLE;
    std::vector<VkFramebuffer> framebuffers;
    VkCommandPool gCmdPool = VK_NULL_HANDLE;
    std::vector<VkCommandBuffer> gCmdBufs;
    {
        // 选择surface格式
        uint32_t fmtCount=0; vkGetPhysicalDeviceSurfaceFormatsKHR(physical, surface, &fmtCount, nullptr);
        std::vector<VkSurfaceFormatKHR> formats(fmtCount);
        vkGetPhysicalDeviceSurfaceFormatsKHR(physical, surface, &fmtCount, formats.data());
        VkSurfaceFormatKHR chosen = formats[0];
        for (auto &f : formats) {
            if (f.format == VK_FORMAT_B8G8R8A8_UNORM) { chosen = f; break; }
        }
        swapFormat = chosen.format;

        // 选择present mode
        uint32_t pmCount=0; vkGetPhysicalDeviceSurfacePresentModesKHR(physical, surface, &pmCount, nullptr);
        std::vector<VkPresentModeKHR> modes(pmCount);
        vkGetPhysicalDeviceSurfacePresentModesKHR(physical, surface, &pmCount, modes.data());
        VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; // 始终可用
        for (auto m : modes) { if (m == VK_PRESENT_MODE_MAILBOX_KHR) { presentMode = m; break; } }

        // 选择extent
        VkSurfaceCapabilitiesKHR caps{};
        vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical, surface, &caps);
        int fbW, fbH; glfwGetFramebufferSize(window, &fbW, &fbH);
        swapExtent = { (uint32_t)fbW, (uint32_t)fbH };
        if (caps.currentExtent.width != UINT32_MAX) swapExtent = caps.currentExtent;

        uint32_t imageCount = caps.minImageCount + 1;
        if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount) imageCount = caps.maxImageCount;

        VkSwapchainCreateInfoKHR sci{VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR};
        sci.surface = surface;
        sci.minImageCount = imageCount;
        sci.imageFormat = swapFormat;
        sci.imageColorSpace = chosen.colorSpace;
        sci.imageExtent = swapExtent;
        sci.imageArrayLayers = 1;
        sci.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
        sci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
        sci.preTransform = caps.currentTransform;
        sci.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
        sci.presentMode = presentMode;
        sci.clipped = VK_TRUE;
        sci.oldSwapchain = VK_NULL_HANDLE;
        VK_CHECK( vkCreateSwapchainKHR(device, &sci, nullptr, &swapchain) );

        uint32_t siCount=0; vkGetSwapchainImagesKHR(device, swapchain, &siCount, nullptr);
        swapImages.resize(siCount);
        vkGetSwapchainImagesKHR(device, swapchain, &siCount, swapImages.data());

        // 图像视图
        swapImageViews.resize(siCount);
        for (uint32_t i=0;i<siCount;++i) {
            VkImageViewCreateInfo iv{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
            iv.image = swapImages[i];
            iv.viewType = VK_IMAGE_VIEW_TYPE_2D;
            iv.format = swapFormat;
            iv.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
            iv.subresourceRange.baseMipLevel = 0;
            iv.subresourceRange.levelCount = 1;
            iv.subresourceRange.baseArrayLayer = 0;
            iv.subresourceRange.layerCount = 1;
            VK_CHECK( vkCreateImageView(device, &iv, nullptr, &swapImageViews[i]) );
        }

        // 渲染通道
        VkAttachmentDescription color{};
        color.format = swapFormat;
        color.samples = VK_SAMPLE_COUNT_1_BIT;
        color.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
        color.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
        color.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
        color.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
        color.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
        color.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;

        VkAttachmentReference colorRef{};
        colorRef.attachment = 0;
        colorRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

        VkSubpassDescription subpass{};
        subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
        subpass.colorAttachmentCount = 1;
        subpass.pColorAttachments = &colorRef;

        VkSubpassDependency dep{};
        dep.srcSubpass = VK_SUBPASS_EXTERNAL;
        dep.dstSubpass = 0;
        dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
        dep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
        dep.srcAccessMask = 0;
        dep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;

        VkRenderPassCreateInfo rpci{VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO};
        rpci.attachmentCount = 1;
        rpci.pAttachments = &color;
        rpci.subpassCount = 1;
        rpci.pSubpasses = &subpass;
        rpci.dependencyCount = 1;
        rpci.pDependencies = &dep;
        VK_CHECK( vkCreateRenderPass(device, &rpci, nullptr, &renderPass) );

        // 帧缓冲
        framebuffers.resize(siCount);
        for (uint32_t i=0;i<siCount;++i) {
            VkImageView attachments[1] = { swapImageViews[i] };
            VkFramebufferCreateInfo fci{VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO};
            fci.renderPass = renderPass;
            fci.attachmentCount = 1;
            fci.pAttachments = attachments;
            fci.width = swapExtent.width;
            fci.height = swapExtent.height;
            fci.layers = 1;
            VK_CHECK( vkCreateFramebuffer(device, &fci, nullptr, &framebuffers[i]) );
        }

        // 图形命令池与缓冲
        VkCommandPoolCreateInfo cpci{VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO};
        cpci.queueFamilyIndex = *computeQueueFamily; // 同一队列族
        cpci.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
        VK_CHECK( vkCreateCommandPool(device, &cpci, nullptr, &gCmdPool) );
        gCmdBufs.resize(siCount);
        VkCommandBufferAllocateInfo cbai{VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO};
        cbai.commandPool = gCmdPool;
        cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
        cbai.commandBufferCount = siCount;
        VK_CHECK( vkAllocateCommandBuffers(device, &cbai, gCmdBufs.data()) );
    }

    // ============ 图形描述符、管线 ============
    VkDescriptorSetLayout gDescLayout = VK_NULL_HANDLE;
    VkDescriptorPool gDescPool = VK_NULL_HANDLE;
    VkDescriptorSet gDescSet = VK_NULL_HANDLE;
    VkPipelineLayout gPipelineLayout = VK_NULL_HANDLE;
    VkPipeline gPipeline = VK_NULL_HANDLE;
    {
        // 描述符布局：combined image sampler at binding 0
        VkDescriptorSetLayoutBinding sb{};
        sb.binding = 0;
        sb.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
        sb.descriptorCount = 1;
        sb.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
        VkDescriptorSetLayoutCreateInfo dsl{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
        dsl.bindingCount = 1;
        dsl.pBindings = &sb;
        VK_CHECK( vkCreateDescriptorSetLayout(device, &dsl, nullptr, &gDescLayout) );

        VkDescriptorPoolSize psize{}; psize.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; psize.descriptorCount = 1;
        VkDescriptorPoolCreateInfo dpci{VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO};
        dpci.maxSets = 1; dpci.poolSizeCount = 1; dpci.pPoolSizes = &psize;
        VK_CHECK( vkCreateDescriptorPool(device, &dpci, nullptr, &gDescPool) );

        VkDescriptorSetAllocateInfo dsai{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO};
        dsai.descriptorPool = gDescPool;
        dsai.descriptorSetCount = 1;
        dsai.pSetLayouts = &gDescLayout;
        VK_CHECK( vkAllocateDescriptorSets(device, &dsai, &gDescSet) );

        VkDescriptorImageInfo dii{};
        dii.sampler = texSampler;
        dii.imageView = texView;
        dii.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
        VkWriteDescriptorSet w{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};
        w.dstSet = gDescSet; w.dstBinding = 0; w.descriptorCount = 1; w.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; w.pImageInfo = &dii;
        vkUpdateDescriptorSets(device, 1, &w, 0, nullptr);

        // 加载图形着色器
        auto loadSpv = [&](const char* path){
            std::ifstream fs(path, std::ios::binary);
            if (!fs) { std::cerr << "Failed to open " << path << "\n"; std::abort(); }
            fs.seekg(0, std::ios::end); size_t sz = fs.tellg(); fs.seekg(0);
            std::vector<char> buf(sz); fs.read(buf.data(), sz); return buf;
        };
        // 从当前目录或可执行目录加载图形着色器
        std::string exePath2 = std::string(argv[0]);
        size_t slash2 = exePath2.find_last_of("/\\");
        std::string exeDir2 = (slash2 != std::string::npos) ? exePath2.substr(0, slash2) : std::string(".");
        auto loadSpvTry = [&](const char* base){
            std::vector<char> data;
            // 先尝试当前目录
            {
                std::ifstream fs(base, std::ios::binary);
                if (fs) { fs.seekg(0, std::ios::end); size_t sz = fs.tellg(); fs.seekg(0); data.resize(sz); fs.read(data.data(), sz); return data; }
            }
            // 再尝试可执行目录
            std::string alt = exeDir2 + "/" + base;
            {
                std::ifstream fs(alt.c_str(), std::ios::binary);
                if (fs) { fs.seekg(0, std::ios::end); size_t sz = fs.tellg(); fs.seekg(0); data.resize(sz); fs.read(data.data(), sz); return data; }
            }
            std::cerr << "Failed to open SPV: " << base << " (also tried: " << alt << ")\n";
            std::abort();
        };
        std::vector<char> vertSpv = loadSpvTry("shader.vert.spv");
        std::vector<char> fragSpv = loadSpvTry("shader.frag.spv");
        VkShaderModule vsm, fsm;
        {
            VkShaderModuleCreateInfo ci{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO};
            ci.codeSize = vertSpv.size(); ci.pCode = reinterpret_cast<const uint32_t*>(vertSpv.data());
            VK_CHECK( vkCreateShaderModule(device, &ci, nullptr, &vsm) );
            ci.codeSize = fragSpv.size(); ci.pCode = reinterpret_cast<const uint32_t*>(fragSpv.data());
            VK_CHECK( vkCreateShaderModule(device, &ci, nullptr, &fsm) );
        }

        // Add push constants for WC/WW, invert, zoom and pan
        VkPushConstantRange pcr{};
        pcr.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
        pcr.offset = 0;
        pcr.size = (uint32_t)(sizeof(float) * 5 + sizeof(int));

        VkPipelineLayoutCreateInfo plci{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
        plci.setLayoutCount = 1; plci.pSetLayouts = &gDescLayout;
        plci.pushConstantRangeCount = 1; plci.pPushConstantRanges = &pcr;
        VK_CHECK( vkCreatePipelineLayout(device, &plci, nullptr, &gPipelineLayout) );

        VkPipelineShaderStageCreateInfo stages[2]{};
        stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
        stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; stages[0].module = vsm; stages[0].pName = "main";
        stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
        stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; stages[1].module = fsm; stages[1].pName = "main";

        VkPipelineVertexInputStateCreateInfo vis{VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO};
        VkPipelineInputAssemblyStateCreateInfo ias{VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO};
        ias.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;

        VkViewport vp{}; vp.x = 0; vp.y = 0; vp.width = (float)swapExtent.width; vp.height = (float)swapExtent.height; vp.minDepth = 0.0f; vp.maxDepth = 1.0f;
        VkRect2D sc{}; sc.offset = {0,0}; sc.extent = swapExtent;
        VkPipelineViewportStateCreateInfo vps{VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO};
        vps.viewportCount = 1; vps.pViewports = &vp; vps.scissorCount = 1; vps.pScissors = &sc;

        VkPipelineRasterizationStateCreateInfo rs{VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO};
        rs.polygonMode = VK_POLYGON_MODE_FILL; rs.cullMode = VK_CULL_MODE_NONE; rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; rs.lineWidth = 1.0f;

        VkPipelineMultisampleStateCreateInfo ms{VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO};
        ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;

        VkPipelineColorBlendAttachmentState cba{}; cba.colorWriteMask = 0xF; cba.blendEnable = VK_FALSE;
        VkPipelineColorBlendStateCreateInfo cbs{VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO};
        cbs.attachmentCount = 1; cbs.pAttachments = &cba;

        VkGraphicsPipelineCreateInfo gpci{VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO};
        gpci.stageCount = 2; gpci.pStages = stages;
        gpci.pVertexInputState = &vis; gpci.pInputAssemblyState = &ias; gpci.pViewportState = &vps;
        gpci.pRasterizationState = &rs; gpci.pMultisampleState = &ms; gpci.pColorBlendState = &cbs;
        gpci.layout = gPipelineLayout; gpci.renderPass = renderPass; gpci.subpass = 0;
        VK_CHECK( vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &gpci, nullptr, &gPipeline) );

        // 着色器模块可立即释放
        vkDestroyShaderModule(device, vsm, nullptr);
        vkDestroyShaderModule(device, fsm, nullptr);
    }

    // 同步对象
    VkSemaphore imgAvail = VK_NULL_HANDLE, renFinish = VK_NULL_HANDLE;
    VkFence inFlight = VK_NULL_HANDLE;
    {
        VkSemaphoreCreateInfo sci{VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
        VK_CHECK( vkCreateSemaphore(device, &sci, nullptr, &imgAvail) );
        VK_CHECK( vkCreateSemaphore(device, &sci, nullptr, &renFinish) );
        // 首帧需要等待的栅栏应初始化为已触发状态，避免在第一轮循环中卡住
        VkFenceCreateInfo fci{VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
        fci.flags = VK_FENCE_CREATE_SIGNALED_BIT;
        VK_CHECK( vkCreateFence(device, &fci, nullptr, &inFlight) );
    }

    // ============ 渲染循环 ============
    // 交互显示参数（归一化WC/WW、反相、缩放与平移）
    struct PushParams {
        float winCenter;
        float winWidth;
        int invert;
        float zoom;
        float offsetX;
        float offsetY;
    } push{autoWC, autoWW, 0, 1.0f, 0.0f, 0.0f};

    while (!glfwWindowShouldClose(window)) {
        glfwPollEvents();
        // 键盘控制：A/D 调中心，W/S 调宽度，I/O 反相切换，+/- 缩放，方向键平移
        if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) push.winCenter -= 0.001f;
        if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) push.winCenter += 0.001f;
        if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) push.winWidth += 0.001f;
        if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) push.winWidth -= 0.001f;
        if (glfwGetKey(window, GLFW_KEY_KP_ADD) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_EQUAL) == GLFW_PRESS) push.zoom *= 1.01f; // '+'
        if (glfwGetKey(window, GLFW_KEY_KP_SUBTRACT) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_MINUS) == GLFW_PRESS) push.zoom /= 1.01f; // '-'
        if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS) push.offsetX -= 0.001f;
        if (glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS) push.offsetX += 0.001f;
        if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS) push.offsetY += 0.001f;
        if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS) push.offsetY -= 0.001f;
        if (glfwGetKey(window, GLFW_KEY_I) == GLFW_PRESS) push.invert = 1;
        if (glfwGetKey(window, GLFW_KEY_O) == GLFW_PRESS) push.invert = 0;
        // 合理约束范围
        push.winWidth = std::max(push.winWidth, 0.001f);
        push.winCenter = std::clamp(push.winCenter, 0.0f, 1.0f);
        push.zoom = std::max(push.zoom, 1e-4f);

        VK_CHECK( vkWaitForFences(device, 1, &inFlight, VK_TRUE, UINT64_MAX) );
        VK_CHECK( vkResetFences(device, 1, &inFlight) );

        uint32_t imageIndex = 0;
        VkResult acq = vkAcquireNextImageKHR(device, swapchain, UINT64_MAX, imgAvail, VK_NULL_HANDLE, &imageIndex);
        if (acq == VK_ERROR_OUT_OF_DATE_KHR) {
            // 简化：不处理重建，继续循环
            continue;
        }

        // 记录命令
        VkCommandBuffer cb = gCmdBufs[imageIndex];
        VK_CHECK( vkResetCommandBuffer(cb, 0) );
        VkCommandBufferBeginInfo bi{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
        VK_CHECK( vkBeginCommandBuffer(cb, &bi) );

        VkClearValue clear; clear.color = { {0.0f, 0.0f, 0.0f, 1.0f} };
        VkRenderPassBeginInfo rpbi{VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO};
        rpbi.renderPass = renderPass; rpbi.framebuffer = framebuffers[imageIndex];
        rpbi.renderArea.offset = {0,0}; rpbi.renderArea.extent = swapExtent;
        rpbi.clearValueCount = 1; rpbi.pClearValues = &clear;
        vkCmdBeginRenderPass(cb, &rpbi, VK_SUBPASS_CONTENTS_INLINE);

        vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, gPipeline);
        vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, gPipelineLayout, 0, 1, &gDescSet, 0, nullptr);
        vkCmdPushConstants(cb, gPipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushParams), &push);
        vkCmdDraw(cb, 3, 1, 0, 0);

        vkCmdEndRenderPass(cb);
        VK_CHECK( vkEndCommandBuffer(cb) );

        VkPipelineStageFlags waitStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
        VkSubmitInfo si{VK_STRUCTURE_TYPE_SUBMIT_INFO};
        si.waitSemaphoreCount = 1; si.pWaitSemaphores = &imgAvail; si.pWaitDstStageMask = &waitStages;
        si.commandBufferCount = 1; si.pCommandBuffers = &cb;
        si.signalSemaphoreCount = 1; si.pSignalSemaphores = &renFinish;
        VK_CHECK( vkQueueSubmit(queue, 1, &si, inFlight) );

        VkPresentInfoKHR pi{VK_STRUCTURE_TYPE_PRESENT_INFO_KHR};
        pi.waitSemaphoreCount = 1; pi.pWaitSemaphores = &renFinish;
        pi.swapchainCount = 1; pi.pSwapchains = &swapchain; pi.pImageIndices = &imageIndex;
        VkResult pr = vkQueuePresentKHR(queue, &pi);
        if (pr == VK_ERROR_OUT_OF_DATE_KHR || pr == VK_SUBOPTIMAL_KHR) {
            // 简化：不处理重建
        }
    }

    // 资源清理（图形）
    vkDeviceWaitIdle(device);
    vkDestroyFence(device, inFlight, nullptr);
    vkDestroySemaphore(device, imgAvail, nullptr);
    vkDestroySemaphore(device, renFinish, nullptr);
    vkDestroyPipeline(device, gPipeline, nullptr);
    vkDestroyPipelineLayout(device, gPipelineLayout, nullptr);
    vkDestroyDescriptorPool(device, gDescPool, nullptr);
    vkDestroyDescriptorSetLayout(device, gDescLayout, nullptr);
    for (auto fb : framebuffers) vkDestroyFramebuffer(device, fb, nullptr);
    vkDestroyRenderPass(device, renderPass, nullptr);
    for (auto v : swapImageViews) vkDestroyImageView(device, v, nullptr);
    vkDestroySwapchainKHR(device, swapchain, nullptr);
    vkDestroyCommandPool(device, gCmdPool, nullptr);
    vkDestroyImageView(device, texView, nullptr);
    vkDestroySampler(device, texSampler, nullptr);
    vkDestroyImage(device, texImage, nullptr);
    vkFreeMemory(device, texMem, nullptr);

    // 关闭窗口
    glfwDestroyWindow(window);
    glfwTerminate();

    // 清理资源（计算）
    vkDestroyFence(device, fence, nullptr);                    // 销毁栅栏
    vkDestroyPipeline(device, pipeline, nullptr);              // 销毁管道
    vkDestroyPipelineLayout(device, pipelineLayout, nullptr);  // 销毁管道布局
    vkDestroyShaderModule(device, shaderModule, nullptr);      // 销毁着色器模块
    vkDestroyDescriptorPool(device, descPool, nullptr);        // 销毁描述符池
    vkDestroyDescriptorSetLayout(device, descLayout, nullptr); // 销毁描述符集布局
    vkDestroyBuffer(device, pixelStagingBuf, nullptr);         // 销毁像素暂存缓冲区
    vkFreeMemory(device, pixelStagingMem, nullptr);            // 释放像素暂存内存
    vkDestroyBuffer(device, pixelDeviceBuf, nullptr);          // 销毁像素设备缓冲区
    vkFreeMemory(device, pixelDeviceMem, nullptr);             // 释放像素设备内存
    vkDestroyBuffer(device, histDeviceBuf, nullptr);           // 销毁直方图设备缓冲区
    vkFreeMemory(device, histDeviceMem, nullptr);              // 释放直方图设备内存
    vkDestroyBuffer(device, histStagingBuf, nullptr);          // 销毁直方图暂存缓冲区
    vkFreeMemory(device, histStagingMem, nullptr);             // 释放直方图暂存内存
    vkDestroyCommandPool(device, cmdPool, nullptr);            // 销毁命令池
    vkDestroyDevice(device, nullptr);                          // 销毁逻辑设备
    vkDestroyInstance(instance, nullptr);                      // 销毁Vulkan实例

    return 0;
}
